package com.xueyi.common.redis.model;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.xueyi.common.core.exception.NoCacheWarnException;
import com.xueyi.common.core.utils.MapUtil;
import com.xueyi.common.core.utils.StringPool;
import com.xueyi.common.core.utils.XueyiStringUtil;
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;

import javax.annotation.PostConstruct;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 缓存模型类
 *
 * @param <T>
 * @author gaorongyi
 */
@SuppressWarnings({"unused, UnusedReturnValue", "WhileLoopReplaceableByForEach"})
public abstract class CacheModel<T extends Serializable> {
    private long expireTime = 24 * 60 * 60;
    private long emptyExpireTime = 10 * 60;
    private final ThreadLocal<Map<String, String>> threadCacheMap = ThreadLocal.withInitial(HashMap::new);
    private final ThreadLocal<Map<String, Field>> fieldMap = ThreadLocal.withInitial(HashMap::new);
    private final ThreadLocal<Map<String, Method>> methodMap = ThreadLocal.withInitial(HashMap::new);
    private String className = null;
    private Class<?> cls = null;

    /**
     * 清空缓存
     */
    public void clearCache() {
        setVersionKey(System.currentTimeMillis());
        resetThreadLocalMap();
        RMap<String, T> cacheMap = getRedisson().getMap(getCacheMapKey());
        if (cacheMap != null && cacheMap.isExists() && cacheMap.size() > 0) {
            cacheMap.deleteAsync();
        }
        Set<String> keys = getRedisTemplate().keys(getCacheMapKey() + XueyiStringUtil.COLON + XueyiStringUtil.STAR);
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                getRedisson().getBucket(key).deleteAsync();
            }
        }
        setVersionKey(System.currentTimeMillis());
    }

    /**
     * 异步清空缓存
     */
    @Async
    public void clearCacheAsync() {
        clearCache();
    }

    /**
     * 重建缓存
     */
    public void rebuildCache() {
        setClassName();
        clearCache();
        List<T> list = getMapper().selectList(null);
        setVersionKey(System.currentTimeMillis());
        rebuildCacheCollection(list);
    }

    private void rebuildCacheCollection(Collection<T> entities) {
        RMap<String, T> cacheMap = getRedisson().getMap(getCacheMapKey());
        String key;
        RBucket<String> bucket;
        for (T temp : entities) {
            key = generateCacheKey(temp);
            cacheMap.put(key, temp);
            bucket = getRedisson().getBucket(getBucketKey(key));
            bucket.set(key);
            bucket.expireAsync(getExpireTime(), TimeUnit.SECONDS);
        }
        setVersionKey(System.currentTimeMillis());
    }

    /**
     * 异步重建缓存
     */
    @Async
    public void rebuildCacheAsync() {
        rebuildCache();
    }

    /**
     * 初始化缓存
     */
    @PostConstruct
    public void initCache() {
        RLock initLock = getRedisson().getLock("CACHE-INIT-LOCK:" + getCacheMapKey());
        try {
            initLock.lock();
            setVersionKey(System.currentTimeMillis());
            checkCacheMapExists();
            setVersionKey(System.currentTimeMillis());
        } finally {
            try {
                initLock.unlock();
            } catch (Exception ignore) {
            }
        }
        rebuildJvmCache();
    }

    /**
     * 缓存指定对象集合
     *
     * @param entities 对象集合
     */
    public void cacheCollection(Collection<T> entities) {
        if (entities == null || entities.size() <= 0) {
            return;
        }
        if (checkCacheMapExists()) {
            rebuildCacheCollection(entities);
        }
    }

    /**
     * 异步缓存指定对象集合
     *
     * @param entities 对象集合
     */
    @Async
    public void cacheCollectionAsync(Collection<T> entities) {
        cacheCollection(entities);
    }

    /**
     * 缓存指定对象
     *
     * @param entity 缓存对象
     */
    public void cacheEntity(T entity) {
        if (entity == null) {
            return;
        }
        removeCacheByEntityPrimaryKey(entity);
        if (checkCacheMapExists()) {
            String key = generateCacheKey(entity);
            getRMap().put(key, entity);
            getRBucket(getBucketKey(key)).set(key);
            setVersionKey(System.currentTimeMillis());
        }
    }

    /**
     * 通过主键删除对应的缓存并返回删除数量，如果传入的对象主键没有值，则直接返回0<br>
     * 主键根据覆写的方法进行获取：{@link #getPrimaryKeyLambda()}
     *
     * @param entity 对象
     * @return count
     */
    private long removeCacheByEntityPrimaryKey(T entity) {
        try {
            Object val = reflectReadVal(reflectField(getPrimaryKeyLambda()), entity);
            if (val != null) {
                return removeCacheLambda(getPrimaryKeyLambda(), val);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            setVersionKey(System.currentTimeMillis());
        }
        return 0;
    }

    /**
     * 异步缓存指定对象
     *
     * @param entity 缓存对象
     */
    @Async
    public void cacheEntityAsync(T entity) {
        cacheEntity(entity);
    }

    /**
     * 根据ID重新缓存对象
     *
     * @param id ID
     * @return {@link T}
     */
    public T cacheEntityById(long id) {
        T entity = getMapper().selectById(id);
        if (checkCacheMapExists()) {
            cacheEntity(entity);
        }
        return entity;
    }

    /**
     * 根据ID重新缓存对象 - 异步
     *
     * @param id ID
     */
    @Async
    public void cacheEntityByIdAsync(long id) {
        cacheEntityById(id);
    }

    /**
     * 根据ID主键删除缓存数据
     */
    public long removeCacheById(long id) {
        Map<SFunction<T, Object>, Object> fnMap = MapUtil.initHashMap(1);
        fnMap.put(getPrimaryKeyLambda(), id);
        return removeCacheLambda(fnMap);
    }

    /**
     * 根据ID主键删除缓存数据 - 异步
     */
    @Async
    public void removeCacheByIdAsync(long id) {
        removeCacheById(id);
    }

    /**
     * 指定字段精确匹配指定值<br>
     * 由于用到{@link ThreadLocal}，此方法禁止多线程调用
     *
     * @param fn  Entity::getFunction()
     * @param val ? extends Object
     */
    public void eqCache(SFunction<T, Object> fn, Object val) {
        Field field = reflectField(fn);
        String fieldName = field.getName();
        // 从field取出字段名，可以根据实际情况调整
        CacheKey cacheKey = field.getAnnotation(CacheKey.class);
        if (cacheKey != null) {
            threadCacheMap.get().put(fieldName, XueyiStringUtil.combineStringDefined(StringPool.COLON, fieldName, val));
        }
    }

    /**
     * 指定字段模糊匹配指定值<br>
     * 由于用到{@link ThreadLocal}，此方法禁止多线程调用
     *
     * @param fn  Entity::getFunction()
     * @param val ? extends Object
     */
    public void likeCache(SFunction<T, Object> fn, Object val) {
        Field field = reflectField(fn);
        String fieldName = field.getName();
        // 从field取出字段名，可以根据实际情况调整
        CacheKey cacheKey = field.getAnnotation(CacheKey.class);
        if (cacheKey != null) {
            threadCacheMap.get().put(fieldName, XueyiStringUtil.combineStringDefined(StringPool.COLON, fieldName, StringPool.STAR + val + StringPool.STAR));
        }
    }

    /**
     * 根据传入的lambda条件获取唯一缓存<br>
     * 由于用到{@link ThreadLocal}，此方法禁止多线程调用
     *
     * @return entity
     */
    public T getCacheLambda() {
        Set<String> keySet = null;
        try {
            keySet = getTargetKeySet();
        } catch (NoCacheWarnException e) {
            System.out.println(e.getMessage());
        }
        return getUnique(keySet);
    }

    /**
     * 根据主键获取指定缓存数据
     *
     * @param val 主键值
     * @return entity
     */
    public T getCacheByPrimaryKey(Object val) {
        return getLambda(getPrimaryKeyLambda(), val);
    }

    /**
     * 根据传入的lambda条件获取缓存集合<br>
     * 由于用到{@link ThreadLocal}，此方法禁止多线程调用
     *
     * @return entity List
     */
    public List<T> listCacheLambda() {
        Set<String> keySet = null;
        try {
            keySet = getTargetKeySet();
        } catch (NoCacheWarnException e) {
            System.out.println(e.getMessage());
        }
        return list(keySet);
    }

    /**
     * 根据传入对象，获取完整缓存KEY，要求CacheKey注解的字段值不能为NULL<br>
     * 由于用到{@link ThreadLocal}，此方法禁止多线程调用
     *
     * @return String
     */
    public String generateCacheKey(T entity) {
        if (cls == null) {
            setClassName();
        }
        try {
            Class<?> superCls = cls.getSuperclass();
            List<Field> fieldList = new ArrayList<>(Arrays.asList(cls.getDeclaredFields()));
            if (superCls != null) {
                fieldList.addAll(Arrays.asList(superCls.getDeclaredFields()));
            }
            for (Field field : fieldList) {
                if (field.getAnnotation(CacheKey.class) != null) {
                    threadCacheMap.get().put(field.getName(), XueyiStringUtil.combineStringDefined(StringPool.COLON, field.getName(), reflectReadVal(field, entity)));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return getKeyByMap();
    }

    /**
     * 拼装返回查询的key<br>
     * 由于用到{@link ThreadLocal}，此方法禁止多线程调用
     *
     * @return String
     */
    public String getFuzzyCacheKey() {
        if (cls == null) {
            setClassName();
        }
        for (Field field : cls.getDeclaredFields()) {
            if (field.getAnnotation(CacheKey.class) != null && !threadCacheMap.get().containsKey(field.getName())) {
                threadCacheMap.get().put(field.getName(), XueyiStringUtil.combineStringDefined(StringPool.COLON, field.getName(), StringPool.STAR));
            }
        }
        return getBucketKey(getKeyByMap());
    }

    /**
     * 获取唯一缓存对象
     *
     * @param fn       lambda函数 - 指定缓存字段
     * @param paramVal 字段值
     * @return {@link T}
     * @throws NoCacheWarnException 表示仅查询到空缓存数据，需要在外部进行捕获处理
     */
    protected T get(SFunction<T, Object> fn, Object paramVal) throws NoCacheWarnException {
        return getUnique(keys(fn, paramVal));
    }

    /**
     * 获取唯一缓存对象
     *
     * @param fnAndParamValMap lambda函数及对应值map
     * @return {@link T}
     * @throws NoCacheWarnException 表示仅查询到空缓存数据，需要在外部进行捕获处理
     */
    protected T get(Map<SFunction<T, Object>, Object> fnAndParamValMap) throws NoCacheWarnException {
        return getUnique(keys(fnAndParamValMap));
    }

    /**
     * 获取唯一缓存对象
     *
     * @param keys 指定缓存key集合
     * @return {@link T}
     */
    private T getUnique(Set<String> keys) {
        checkCacheMapExists();
        if (keys != null && keys.size() > 0) {
            if (keys.size() == 1) {
                RMap<String, T> rMap = getRMap();
                String targetKey = keys.iterator().next();
                return rMap.get(targetKey);
            } else {
                throw new RuntimeException("NOT UNIQUE RESULT " + JSON.toJSON(keys));
            }
        }
        return null;
    }

    /**
     * 获取缓存对象List
     *
     * @param fn       lambda函数 - 指定缓存字段
     * @param paramVal 字段值
     * @return List {@link T}
     * @throws NoCacheWarnException 表示仅查询到空缓存数据，需要在外部进行捕获处理
     */
    protected List<T> list(SFunction<T, Object> fn, Object paramVal) throws NoCacheWarnException {
        checkCacheMapExists();
        Set<String> keys = keys(fn, paramVal);
        if (keys != null && keys.size() > 0) {
            RMap<String, T> rMap = getRMap();
            return new ArrayList<>(rMap.getAll(keys).values());
        }
        return null;
    }

    /**
     * 获取缓存对象List
     *
     * @param keys 指定缓存key集合
     * @return List {@link T}
     */
    protected List<T> list(Set<String> keys) {
        checkCacheMapExists();
        List<T> result = null;
        if (keys != null && keys.size() > 0) {
            RMap<String, T> rMap = getRMap();
            Map<String, T> resultMap = rMap.getAll(keys);
            if (resultMap != null && resultMap.size() > 0) {
                result = new ArrayList<>(resultMap.values());
            }
        }
        return result;
    }

    /**
     * 获取所有缓存对象List
     *
     * @return List {@link T}
     */
    protected List<T> listAll() {
        checkCacheMapExists();
        RMap<String, T> rMap = getRMap();
        return new ArrayList<>(rMap.readAllValues());
    }

    /**
     * 模糊匹配缓存KEY
     *
     * @param fn       lambda函数 - 指定缓存字段
     * @param paramVal 字段值
     * @return 匹配成功的KEY SET
     * @throws NoCacheWarnException 表示仅查询到空缓存数据，需要在外部进行捕获处理
     */
    protected Set<String> keys(SFunction<T, Object> fn, Object paramVal) throws NoCacheWarnException {
        checkCacheMapExists();
        this.eqCache(fn, paramVal);
        return getTargetKeySet();
    }

    /**
     * 模糊匹配缓存KEY
     *
     * @param fnAndParamValMap lambda函数及对应值map
     * @return 匹配成功的KEY SET
     * @throws NoCacheWarnException 表示仅查询到空缓存数据，需要在外部进行捕获处理
     */
    protected Set<String> keys(Map<SFunction<T, Object>, Object> fnAndParamValMap) throws NoCacheWarnException {
        checkCacheMapExists();
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnAndParamValMap.entrySet()) {
            this.eqCache(entry.getKey(), entry.getValue());
        }
        return getTargetKeySet();
    }

    /**
     * 检查缓存是否存在，若不存在则重建缓存
     */
    private boolean checkCacheMapExists() {
        RMap<String, T> rMap = getRedisson().getMap(getCacheMapKey());
        if (rMap == null || (!rMap.isExists()) || rMap.size() <= 0) {
            rebuildCache();
            return false;
        } else {
            return true;
        }
    }

    /**
     * 根据传入的lambda函数生成模糊key，然后根据模糊key生成精确key集合
     *
     * @return 缓存key集合
     * @throws NoCacheWarnException 表示仅查询到空缓存数据，需要在外部进行捕获处理
     */
    private Set<String> getTargetKeySet() throws NoCacheWarnException {
        String targetKey = getFuzzyCacheKey();
        Set<String> result = null;
        Set<String> keys = getRedisTemplate().keys(targetKey);
        RBucket<String> bucket;
        String realKey;
        boolean existsKey = false;
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                existsKey = true;
                bucket = getRBucket(key);
                realKey = bucket.get();
                if (!XueyiStringUtil.EMPTY.equals(realKey)) {
                    bucket.expireAsync(getExpireTime(), TimeUnit.SECONDS);
                    if (result == null) {
                        result = new HashSet<>();
                    }
                    result.add(realKey);
                } else {
                    bucket.expireAsync(getEmptyExpireTime(), TimeUnit.SECONDS);
                }
            }
        }
        if (existsKey && (result == null || result.size() <= 0)) {
            throw new NoCacheWarnException(targetKey);
        }
        return result;
    }

    /**
     * 获取bucket KEY，用于缓存Map索引方便模糊搜索
     *
     * @param key 缓存Map KEY
     * @return String
     */
    private String getBucketKey(String key) {
        return XueyiStringUtil.combineStringDefined(XueyiStringUtil.COLON, getCacheMapKey(), key);
    }

    /**
     * 针对传入的CacheMapName进行最外层封装，作为存入redis的KEY
     *
     * @return Cache Map Key
     */
    public String getCacheMapKey() {
        setClassName();
        return XueyiStringUtil.combineStringDefined(XueyiStringUtil.COLON, "Cache", getClassName());
    }

    public String getCacheMapValue(String mapKey) {
        return mapKey.replaceFirst(getCacheMapKey() + XueyiStringUtil.COLON, XueyiStringUtil.EMPTY);
    }

    /**
     * 利用单个lambda表达式进行条件检索并返回缓存数据
     *
     * @param fn  lambda函数
     * @param val 值
     * @return 缓存集合
     */
    protected List<T> listLambda(SFunction<T, Object> fn, Object val) {
        List<T> result = null;
        try {
            result = list(fn, val);
            if (result == null || result.size() <= 0) {
                return queryList(fn, val);
            }
        } catch (NoCacheWarnException e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

    /**
     * 利用多个lambda表达式进行条件检索并返回缓存数据
     *
     * @param fnMap lambda函数map
     * @return 缓存集合
     */
    protected List<T> listLambda(Map<SFunction<T, Object>, Object> fnMap) {
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnMap.entrySet()) {
            eqCache(entry.getKey(), entry.getValue());
        }
        List<T> result = null;
        try {
            result = list(getTargetKeySet());
            if (result == null || result.size() <= 0) {
                return queryList(fnMap);
            }
        } catch (NoCacheWarnException e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

    /**
     * 利用单个lambda表达式进行条件检索并返回缓存数据
     *
     * @param fn  lambda函数
     * @param val 值
     * @return 缓存对象
     */
    protected T getLambda(SFunction<T, Object> fn, Object val) {
        T result = null;
        try {
            result = get(fn, val);
            if (result == null) {
                return queryGet(fn, val);
            }
        } catch (NoCacheWarnException e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

    /**
     * 利用多个lambda表达式进行条件检索并返回缓存数据
     *
     * @param fnMap lambda函数map
     * @return 缓存对象
     */
    protected T getLambda(Map<SFunction<T, Object>, Object> fnMap) {
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnMap.entrySet()) {
            eqCache(entry.getKey(), entry.getValue());
        }
        T result = null;
        try {
            result = getUnique(getTargetKeySet());
            if (result == null) {
                return queryGet(fnMap);
            }
        } catch (NoCacheWarnException e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

    /**
     * 根据传入的lambda函数条件删除缓存
     *
     * @param fn  lambda函数条件
     * @param val lambda值
     * @return count
     */
    public long removeCacheLambda(SFunction<T, Object> fn, Object val) {
        Map<SFunction<T, Object>, Object> fnMap = MapUtil.initHashMap(1);
        fnMap.put(fn, val);
        return removeCacheLambda(fnMap);
    }

    /**
     * 根据传入的lambda函数条件删除缓存
     *
     * @param fnMap lambda函数条件
     * @return count
     */
    public long removeCacheLambda(Map<SFunction<T, Object>, Object> fnMap) {
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnMap.entrySet()) {
            eqCache(entry.getKey(), entry.getValue());
        }
        String fuzzyKey = getFuzzyCacheKey();
        Set<String> mapKeys = new HashSet<>();
        int deleteIndexCount = 0;
        Set<String> keys = getRedisTemplate().keys(fuzzyKey);
        if (keys != null && keys.size() > 0) {
            setVersionKey(System.currentTimeMillis());
            for (String key : keys) {
                mapKeys.add(getCacheMapValue(key));
                getRBucket(key).deleteAsync();
            }
        }
        long count = mapKeys.size() == 0 ? 0 : getRMap().fastRemove(mapKeys.toArray(new String[0]));
        setVersionKey(System.currentTimeMillis());
        return count;
    }

    /**
     * 根据传入的lambda函数条件删除缓存
     *
     * @param fn  lambda函数条件
     * @param val lambda值
     */
    public void removeCacheLambdaAsync(SFunction<T, Object> fn, Object val) {
        removeCacheLambda(fn, val);
    }

    /**
     * 根据传入的lambda函数条件删除缓存
     *
     * @param fnMap lambda函数条件
     */
    public void removeCacheLambdaAsync(Map<SFunction<T, Object>, Object> fnMap) {
        removeCacheLambda(fnMap);
    }

    /**
     * 获取缓存Map
     *
     * @return {@link RMap}
     */
    private RMap<String, T> getRMap() {
        checkCacheMapExists();
        return getRedisson().getMap(getCacheMapKey());
    }

    /**
     * 获取用于模糊查询的KEY Bucket缓存
     *
     * @param key Bucket缓存KEY
     * @return {@link RBucket}
     */
    private RBucket<String> getRBucket(String key) {
        return getRedisson().getBucket(key);
    }

    /**
     * 重置ThreadLocal Map
     */
    private void resetThreadLocalMap() {
        if (threadCacheMap.get() != null) {
            threadCacheMap.get().clear();
        } else {
            threadCacheMap.set(MapUtil.initHashMap());
        }
    }

    /**
     * 根据ThreadLocal Map内容生成key并返回
     *
     * @return String - key
     */
    private String getKeyByMap() {
        String result = null;
        for (Map.Entry<String, String> entry : threadCacheMap.get().entrySet()) {
            if (XueyiStringUtil.isEmpty(result)) {
                result = entry.getValue();
            } else {
                result = XueyiStringUtil.combineStringColon(result, entry.getValue());
            }
        }
        resetThreadLocalMap();
        return result;
    }

    /**
     * 获取空缓存超时时间（秒, 默认10分钟）
     *
     * @return long
     */
    public long getEmptyExpireTime() {
        return this.emptyExpireTime;
    }

    /**
     * 设置空缓存超时时间（秒, 默认10分钟）<br>
     * 覆写此方法以改变缓存超时策略
     *
     * @param emptyExpireTime long - 秒
     */
    protected void setEmptyExpireTime(long emptyExpireTime) {
        this.emptyExpireTime = emptyExpireTime;
    }

    /**
     * 获取缓存超时时间（秒, 默认24小时）
     *
     * @return long
     */
    public long getExpireTime() {
        return this.expireTime;
    }

    /**
     * 设置缓存超时时间（秒, 默认24小时）<br>
     * 覆写此方法以改变缓存超时策略
     *
     * @param expireTime long - 秒
     */
    protected void setExpireTime(long expireTime) {
        this.expireTime = expireTime;
    }

    /**
     * 泛型类名，用于生成缓存KEY
     *
     * @return {@link String}
     */
    private String getClassName() {
        return this.className;
    }

    /**
     * 获取泛型类并存储类型及名称
     */
    @SuppressWarnings("unchecked")
    @PostConstruct
    private void setClassName() {
        if (this.cls == null || XueyiStringUtil.isEmpty(this.className)) {
            this.cls = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            this.className = this.cls.getSimpleName();
        }
    }

    /**
     * 穿透list查询
     *
     * @param fn  {@link SFunction}
     * @param val {@link Object}
     * @return List
     */
    private List<T> queryList(SFunction<T, Object> fn, Object val) {
        LambdaQueryWrapper<T> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(fn, val);
        List<T> result = getMapper().selectList(queryWrapper);
        if (result == null || result.size() <= 0) {
            setEmptyCache(fn, val);
        } else {
            cacheCollection(result);
        }
        return result;
    }

    /**
     * 穿透list查询
     *
     * @param fnMap map - key: {@link SFunction}, value: {@link Object}
     * @return List
     */
    private List<T> queryList(Map<SFunction<T, Object>, Object> fnMap) {
        LambdaQueryWrapper<T> queryWrapper = Wrappers.lambdaQuery();
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnMap.entrySet()) {
            queryWrapper.eq(entry.getKey(), entry.getValue());
        }
        List<T> result = getMapper().selectList(queryWrapper);
        if (result == null || result.size() <= 0) {
            setEmptyCache(fnMap);
        } else {
            cacheCollection(result);
        }
        return result;
    }

    /**
     * 穿透get查询
     *
     * @param fn  {@link SFunction}
     * @param val {@link Object}
     * @return entity
     */
    private T queryGet(SFunction<T, Object> fn, Object val) {
        LambdaQueryWrapper<T> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(fn, val);
        T result = getMapper().selectOne(queryWrapper);
        if (result == null) {
            setEmptyCache(fn, val);
        } else {
            cacheEntity(result);
        }
        return result;
    }

    /**
     * 穿透get查询
     *
     * @param fnMap map - key: {@link SFunction}, value: {@link Object}
     * @return entity
     */
    private T queryGet(Map<SFunction<T, Object>, Object> fnMap) {
        LambdaQueryWrapper<T> queryWrapper = Wrappers.lambdaQuery();
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnMap.entrySet()) {
            queryWrapper.eq(entry.getKey(), entry.getValue());
        }
        T result = getMapper().selectOne(queryWrapper);
        if (result == null) {
            setEmptyCache(fnMap);
        } else {
            cacheEntity(result);
        }
        return result;
    }

    /**
     * 空缓存策略
     *
     * @param fn  {@link SFunction}
     * @param val {@link Object}
     */
    private void setEmptyCache(SFunction<T, Object> fn, Object val) {
        eqCache(fn, val);
        String fuzzyKey = getFuzzyCacheKey();
        RBucket<String> bucket = getRedisson().getBucket(fuzzyKey);
        bucket.set(XueyiStringUtil.EMPTY);
        bucket.expireAsync(getEmptyExpireTime(), TimeUnit.SECONDS);
    }

    /**
     * 空缓存策略
     *
     * @param fnMap map - key: {@link SFunction}, value: {@link Object}
     */
    private void setEmptyCache(Map<SFunction<T, Object>, Object> fnMap) {
        for (Map.Entry<SFunction<T, Object>, Object> entry : fnMap.entrySet()) {
            eqCache(entry.getKey(), entry.getValue());
        }
        String fuzzyKey = getFuzzyCacheKey();
        RBucket<String> bucket = getRedisson().getBucket(fuzzyKey);
        bucket.set(XueyiStringUtil.EMPTY);
        bucket.expireAsync(getEmptyExpireTime(), TimeUnit.SECONDS);
    }

    /**
     * 反射获取对象指定Field值
     *
     * @param field  {@link Field}
     * @param entity {@link T}
     * @return Object
     * @throws Exception e
     */
    private Object reflectReadVal(Field field, T entity) throws Exception {
        String fieldName = field.getName();
        if (methodMap.get().containsKey(fieldName)) {
            return methodMap.get().get(fieldName).invoke(entity);
        }
        PropertyDescriptor descriptor = new PropertyDescriptor(fieldName, entity.getClass());
        Method readMethod = descriptor.getReadMethod();
        methodMap.get().put(fieldName, readMethod);
        return readMethod.invoke(entity);
    }

    /**
     * 根据lambda参数反射获取{@link Field}
     *
     * @param fn lambda参数
     * @return {@link Field}
     */
    private Field reflectField(SFunction<T, Object> fn) {
        // 从function取出序列化方法
        Method writeReplaceMethod;
        try {
            writeReplaceMethod = fn.getClass().getDeclaredMethod("writeReplace");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        // 从序列化方法取出序列化的lambda信息
        boolean isAccessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(true);
        SerializedLambda serializedLambda;
        try {
            serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(fn);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        writeReplaceMethod.setAccessible(isAccessible);

        // 从lambda信息取出method、field、class等
        String fieldName = serializedLambda.getImplMethodName().substring("get".length());
        fieldName = fieldName.replaceFirst(fieldName.charAt(0) + "", (fieldName.charAt(0) + "").toLowerCase());
        if (fieldMap.get().containsKey(fieldName)) {
            return fieldMap.get().get(fieldName);
        }
        Field field;
        try {
            field = Class.forName(serializedLambda.getImplClass().replace("/", ".")).getDeclaredField(fieldName);
        } catch (ClassNotFoundException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
        fieldMap.get().put(fieldName, field);
        return field;
    }

    protected Long getVersionKey() {
        RBucket<Long> bucket = getRedisson().getBucket(getVersionKeyCacheKey());
        return bucket.get();
    }

    protected void setVersionKey(long versionKey) {
        RBucket<Long> bucket = getRedisson().getBucket(getVersionKeyCacheKey());
        bucket.set(versionKey);
    }

    private String getVersionKeyCacheKey() {
        return XueyiStringUtil.combineStringDefined(XueyiStringUtil.COLON, "versionKey", getClassName());
    }

    protected abstract void rebuildJvmCache();

    /**
     * 获取主键lambda函数
     *
     * @return {@link SFunction}
     */
    protected abstract SFunction<T, Object> getPrimaryKeyLambda();

    /**
     * 用于获取Redisson操作对象
     *
     * @return {@link Redisson}
     */
    protected abstract Redisson getRedisson();

    /**
     * 用于获取RedisTemplate操作对象
     *
     * @return {@link StringRedisTemplate}
     */
    protected abstract StringRedisTemplate getRedisTemplate();

    /**
     * 传入dao对象，用于数据库穿透查询
     *
     * @return ? extends BaseMapper
     */
    protected abstract BaseMapper<T> getMapper();
}
